$import( "SmitearWebGL.Objects.Parser.Collada.DaeEntity" );
$import( "SmitearWebGL.Objects.Parser.Collada.DaeVertexWeights" );
$import( "SmitearWebGL.Objects.Parser.Collada.DaeSource" );
$import( "SmitearWebGL.Objects.Parser.Collada.DaeInput" );
$import( "SmitearWebGL.Objects.Parser.Collada.DaeBlendWeight" );

SmitearWebGL.Objects.Parser.Collada.DaeSkin = SmitearWebGL.Objects.Parser.Collada.DaeEntity.extend(
{
	/******** constructor ********/

	init : function( document, node )
	{
		this.bind_shape_matrix = null;
		this.source = "";
		this.joints = null;
		this.bind_matrices = null;
		this.vertex_weights = null;
		this.blendWeightsByJointID = null;
		this.jointsType = "";

		this._super( document, node );
		this._registClass( "SmitearWebGL.Objects.Parser.Collada.DaeSkin" );
	}
	,
	
	/******** public method ********/
	
	read : function( node )
	{
		$THROW( node.localName == SmitearWebGL.Objects.Parser.Collada.Const.DAE_CONTROLLER_SKIN_ELEMENT,
		"expected a '" + SmitearWebGL.Objects.Parser.Collada.Const.DAE_CONTROLLER_SKIN_ELEMENT + "' element" );

		this.joints = [];
		this.vertex_weights = [];
		this.bind_matrices = [];

		this._super( node );

		// required - ref to skin's geometry
		this.source = this.getAttribute(node, SmitearWebGL.Objects.Parser.Collada.Const.DAE_SOURCE_ATTRIBUTE);

		$LOG( "reading skin, source: " + this.source );

		// optional - bind_shape_matrix, defaults to identity matrix
		var bindList = this.getNodeList(node, SmitearWebGL.Objects.Parser.Collada.Const.DAE_BINDSHAPEMX_SKIN_PARAMETER);
		if( bindList.length )
			this.bind_shape_matrix = this.getFloats( bindList[0] );
		else
			this.bind_shape_matrix = [1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1];

		// 3 or more <source> elements
		var sourceList = this.getNodeList(node, SmitearWebGL.Objects.Parser.Collada.Const.DAE_SOURCE_ELEMENT);
		$THROW( sourceList.length >= 3, "<skin> requires a minimum of 3 <source> elements!" );

		// exactly 1 <joints> element
		var jointsNode = this.getNode(node, SmitearWebGL.Objects.Parser.Collada.Const.DAE_JOINTS_ELEMENT);
		$THROW( jointsNode, "need exactly one <joints> element!" );

		// exactly 1 <vertex_weights> element
		var weightsNode = this.getNode(node, SmitearWebGL.Objects.Parser.Collada.Const.DAE_WEIGHTS_ELEMENT);
		$THROW( weightsNode, "need exactly one <vertex_weights> element!"  );

		var jointsList = this.getNodeList(jointsNode, SmitearWebGL.Objects.Parser.Collada.Const.DAE_INPUT_ELEMENT);
		var weights = new SmitearWebGL.Objects.Parser.Collada.DaeVertexWeights(this.document, weightsNode );

		var srcNode;
		var input;

		// fetch sources for <joints>
		var src;
		for ( var i = 0; i < jointsList.length; ++ i )
		{
			var inputNode = jointsList[i];
			input = new SmitearWebGL.Objects.Parser.Collada.DaeInput(this.document, inputNode );
			srcNode = this.getNodeById( node, SmitearWebGL.Objects.Parser.Collada.Const.DAE_SOURCE_ELEMENT, input.source );
			$THROW( srcNode, "source not found! (id='" + input.source + "')" );

			src = new SmitearWebGL.Objects.Parser.Collada.DaeSource( this.document, srcNode );

			switch( input.semantic )
			{
				case SmitearWebGL.Objects.Parser.Collada.Const.DAE_JOINT_SKIN_INPUT:
					this.joints = src.values;
					this.jointsType = src.accessor.params[SmitearWebGL.Objects.Parser.Collada.Const.DAE_JOINT_SKIN_INPUT];
					$LOG( " => => joints: " + this.joints );
					break;

				case SmitearWebGL.Objects.Parser.Collada.Const.DAE_BINDMATRIX_SKIN_INPUT:
					this.bind_matrices = src.values;
					$LOG( " => => bind_matrices: " + this.bind_matrices );
					break;

				default:
					break;
			}
		}

		var maxOffset = 0;
		var jointOffset = 0;
		var weightOffset = 1;
		var tmpWeights = null;

		// fetch sources for <vertex_weights>
		for ( var i = 0; i < weights.inputs.length; ++ i )
		{
			input = weights.inputs[i];
			srcNode = this.getNodeById( node, SmitearWebGL.Objects.Parser.Collada.Const.DAE_SOURCE_ELEMENT, input.source );
			$THROW( srcNode, "source not found! (id='" + input.source + "')" );

			src = new SmitearWebGL.Objects.Parser.Collada.DaeSource( this.document, srcNode );

			switch( input.semantic )
			{
				case SmitearWebGL.Objects.Parser.Collada.Const.DAE_JOINT_SKIN_INPUT:
					jointOffset = input.offset;
					maxOffset++;
					break;

				case SmitearWebGL.Objects.Parser.Collada.Const.DAE_WEIGHT_SKIN_INPUT:
					tmpWeights = src.values;
					weightOffset = input.offset;
					maxOffset++;
					break;

				default:
					break;
			}
		}

		var cur = 0;

		for( var i = 0; i < weights.vcounts.length; i++ )
		{
			var vcount = weights.vcounts[i];

			var tmp = [];

			for( var j = 0; j < vcount; j++ )
			{
				var jidx = weights.v[cur + jointOffset];
				var widx = weights.v[cur + weightOffset];

				var w = tmpWeights[ widx ];
				var jnt = jidx < 0 ? null : this.joints[ jidx ];

				var blendWeight = new SmitearWebGL.Objects.Parser.Collada.DaeBlendWeight( i, jnt, w );
				tmp.push( blendWeight );

				cur += maxOffset;
			}

			this.vertex_weights[i] = tmp;
		}

		$LOG( " => => #vertex_weights " + vertex_weights.length );
	}
	,
	findJointBindMatrix : function( node )
	{
		var id = this.jointsType == "IDREF" ? node.id : node.sid;

		for( var i = 0; i < this.joints.length; i++ )
		{
			if( id == this.joints[i] )
			return this.bind_matrices[i];
		}
		return null;
	}
	,
	findJointBindMatrix2 : function( id )
	{
		for( var i = 0; i < this.joints.length; i++ )
		{
			if( id == this.joints[i] )
			return this.bind_matrices[i];
		}
		return null;
	}
	,
	findJointVertexWeights : function( node )
	{
		var id = this.jointsType == "IDREF" ? node.id : node.sid;

		var weights = [];
		for( var i = 0; i < this.vertex_weights.length; i++  )
		{
			var arr = this.vertex_weights[i];
			for( var j = 0; j < arr.length; j++ )
			if( arr[j].joint == id )
			weights.push( arr[j] );
		}
		return weights;
	}
	,
	findJointVertexWeightsByIDOrSID : function( id )
	{
		var weights = [];
		for( var i = 0; i < this.vertex_weights.length; i++  )
		{
			var arr = this.vertex_weights[i];
			for( var j = 0; j < arr.length; j++ )
			if( arr[j].joint == id )
			weights.push( arr[j] );
		}
		return weights;
	}
	,
	normalizeBlendWeights : function( blendWeights )
	{
		var i, j;

		for( i = 0; i < blendWeights.length; i++  )
		{
			var arr = blendWeights[i];

			var weightSum = 0.0;
			for( j = 0; j < arr.length; j++ )
			{
				weightSum += arr[j].weight;
				arr[j].originalWeight = arr[j].weight;
			}
			if( weightSum == 0.0 || weightSum == 1.0 ) continue;

			var invWeightSum = 1.0 / weightSum;

			for( j = 0; j < arr.length; j++ )
			arr[j].weight *= invWeightSum;
		}
	}
}
);